﻿import { Map } from "./types";

let defaultMaxCount: number = 5000;
let instances: Map<any> = {};
let maxCountMap: Map<number> = {};
let poolsMap: Map<BaseObject[]> = {};

export function setMaxCount(clazz: { new (): BaseObject }, maxCount: number): void {
    if (maxCount < 0 || maxCount !== maxCount) {
        maxCount = 0;
    }

    if (clazz) {
        const classType = String(clazz);
        maxCountMap[classType] = maxCount;
        const pool = poolsMap[classType];
        if (pool && pool.length > maxCount) {
            pool.length = maxCount;
        }
    }
    else {
        defaultMaxCount = maxCount;
        for (let classType in poolsMap) {
            if (!maxCountMap[classType]) {
                continue;
            }

            maxCountMap[classType] = maxCount;
            const pool = poolsMap[classType];
            if (pool.length > maxCount) {
                pool.length = maxCount;
            }
        }
    }
}

export function clearPool(clazz: { new (): BaseObject } | null): void {
    if (clazz) {
        const pool = poolsMap[String(clazz)];
        if (pool && pool.length) {
            pool.length = 0;
        }
    }
    else {
        for (let k in poolsMap) {
            const pool = poolsMap[k];
            pool.length = 0;
        }
    }
}

export class Set<T extends any> extends Array<T> {
    [k: number]: T;
    public constructor(public key: string = "name") {
        super();
    }

    public get(value: any): T | null {
        let i = this.length;
        while (i--) {
            const eachData = this[i];
            if (eachData[this.key] === value) {
                return eachData;
            }
        }

        return null;
    }

    public add(data: T, replace: boolean = false): boolean {
        const index = this.indexOf(data);
        if (index >= 0) {
            if (replace) {
                this.splice(index, 1);
            }
            else {
                return false;
            }
        }
        else {
            let i = this.length;
            while (i--) {
                const eachData = this[i];
                if (eachData[this.key] === data[this.key]) {
                    if (replace) {
                        this.splice(i, 1);
                    }
                    else {
                        return false;
                    }
                }
            }
        }

        this.push(data);
        return true;
    }

    public remove(data: T): void {
        const index = this.indexOf(data);
        if (index >= 0) {
            this.splice(index, 1);
        }
    }

    public removeByValue(value: any): T | null {
        let i = this.length;
        while (i--) {
            const eachData = this[i];
            if (eachData[this.key] === value) {
                this.splice(i, 1);
                return eachData;
            }
        }

        return null;
    }
}

export abstract class BaseObject {
    private static _hashCode: number = 0;

    public static toString(): string {
        throw new Error();
    }

    public static getInstance<T extends BaseObject>(clazz: { new (): T }): T {
        const classType = String(clazz);
        let instance = instances[classType];
        if (!instance) {
            instance = instances[classType] = BaseObject.create(clazz);
        }

        return instance;
    }

    public static create<T extends BaseObject>(clazz: { new (): T }): T {
        const pool = poolsMap[String(clazz)];
        if (pool && pool.length > 0) {
            const object = pool.pop() as T;

            return object;
        }

        const object = new clazz();
        object._onClear();

        return object;
    }

    private static _returnToPool(object: BaseObject): void {
        const classType = String(object.constructor);
        const maxCount = classType in maxCountMap ? defaultMaxCount : maxCountMap[classType];
        const pool = poolsMap[classType] = poolsMap[classType] || [];

        if (pool.length < maxCount) {
            console.assert(pool.indexOf(object) < 0);
            pool.push(object);
        }
    }

    public hashCode: number = BaseObject._hashCode++;

    protected abstract _onClear(): void;

    public returnToPool(): void {
        this._onClear();
        BaseObject._returnToPool(this);
    }

    public dispose(): void {
        this._onClear();
    }
}

export function copyFrom(object: any, target: any): void {
    //console.assert();
    // for (let key in object) {
    //     // constructor
    //     if (key in target) {
    //         const typeO = typeof object[key];
    //         const typeT = typeof target[key];
    //         this._copyFrom(object, key, target[key]);
    //     }
    // }
}

    // protected _copyFrom(object: any, key: any, value: any, maxDepth: number, currentDepth: number): void {
    //     const type = typeof value;
    //     if (type === "function") {
    //     }
    //     else if (type !== "object" || value === null) {
    //         object[key] = value;
    //     }
    //     else if (value instanceof Array) {
    //         let objectValue = object[key]; // 应该同型
    //         objectValue.length = value.length;

    //         for (let i = 0, l = objectValue.length; i < l; ++i) {
    //             this._copyFrom(objectValue, i, value[i], maxDepth, currentDepth);
    //         }
    //     }
    //     else if (value instanceof BaseObject) {
    //         if (maxDepth <= 0 || currentDepth < maxDepth - 1) {
    //             let objectValue = object[key];
    //             if (!objectValue) {
    //                 // objectValue = BaseObject.borrowObject(value.constructor as any);
    //                 // object[key] = objectValue;
    //             }

    //             objectValue.copyFrom(value, maxDepth, currentDepth + 1);
    //         }
    //         else {
    //             object[key] = value;
    //         }
    //     }
    //     else {
    //         const map = object.constructor["COPY_MAP"];
    //         if (map && key in map) {
    //             // const objectValue = BaseObject.borrowObject(map[key]);
    //             // objectValue.copyFrom(value, maxDepth, currentDepth);
    //             // object[key] = objectValue;
    //         }
    //         else {
    //             //BaseObject.copyFrom(object[key] = {}, value);
    //         }
    //     }